package tailf

import (
	"context"
	"github.com/hpcloud/tail"
	"log"
	"time"
)

type TailFileTask struct {
	ctx        context.Context
	cancelFunc context.CancelFunc
	Filename   string      // 监听日志文件路径
	tail       *tail.Tail  // 监听日志文件对象
	Buffer     chan string // 发送缓冲
}

func (tf *TailFileTask) doWork() {
	var line *tail.Line
	var ok bool
	for {
		select {
		// 生命周期 Stop 方法被调用, 准备停止, 清理动作
		case <-tf.ctx.Done():
			err := tf.tail.Stop()
			if err != nil {
				log.Printf("停止监听日志文件 %s 失败, 错误信息: %v\n",
					tf.Filename, err)
			}
			tf.tail.Cleanup()
			log.Printf("停止监听日志文件 %s\n",
				tf.Filename)
			return
		default:
			line, ok = <-tf.tail.Lines
			if !ok {
				log.Printf("收集 %s 文件时发生错误, 暂停1秒钟\n",
					tf.Filename)
				time.Sleep(time.Second)
				continue
			}
			tf.Buffer <- line.Text
		}
	}
}

// Start 异步启动收集
func (tf *TailFileTask) Start() {
	go tf.doWork()
}

// Stop 停止当前收集任务
func (tf *TailFileTask) Stop() {
	tf.cancelFunc()
}

// New 工厂函数, 创建收集任务
func New(filename string, bufferSize int) (*TailFileTask, error) {
	config := tail.Config{
		ReOpen:    true,
		Follow:    true,
		Location:  &tail.SeekInfo{Offset: 0, Whence: 2},
		MustExist: false,
		Poll:      true,
	}

	tailFile, err := tail.TailFile(filename, config)
	if err != nil {
		return nil, err
	}
	// ☆ 对于定义了生命周期的类型, 不应该再使用 context 控制对象内的
	// goroutine, 而是通过生命周期方法控制内部的goroutine
	ctx, cancel := context.WithCancel(context.Background())
	return &TailFileTask{
		ctx:        ctx,
		cancelFunc: cancel,
		Filename:   filename,
		tail:       tailFile,
		Buffer:     make(chan string, bufferSize),
	}, nil
}
